#include <tk.h>
#include <tcl.h>

#define TCL_FUNCTION_SIGNITURE ClientData clientData, Tcl_Interp *pInterp, int argc, char ** argv

typedef char * TKC_WIDGET;
unsigned int tkc_widget_count = 0;

Tcl_Interp  * interp;
Tcl_Channel tkc_stdout;

// ---------------------------------------------------------------------------------- Init and quit functions
int        tkc_init(); // Initialises tkc
int        tkc_mainloop(); // Initialises mainloop
int        tkc_puts(char * output); // Prints a string to the console with a new line
int        tkc_execute(char * format/*                     ...*/); // Executes a formatted command
int        tkc_configure(TKC_WIDGET widget, char * format/*...*/); // Configures a formatted options string
TKC_WIDGET tkc_button(char * format/*     ...*/); // Creates a button       with a formatted options string
TKC_WIDGET tkc_canvas(char * format/*     ...*/); // Creates a canvas       with a formatted options string
TKC_WIDGET tkc_checkbutton(char * format/*...*/); // Creates a checkbutton  with a formatted options string
TKC_WIDGET tkc_entry(char * format/*      ...*/); // Creates a entry        with a formatted options string
TKC_WIDGET tkc_frame(char * format/*      ...*/); // Creates a frame        with a formatted options string
TKC_WIDGET tkc_label(char * format/*      ...*/); // Creates a label        with a formatted options string
TKC_WIDGET tkc_labelframe(char * format/* ...*/); // Creates a labelframe   with a formatted options string
TKC_WIDGET tkc_listbox(char * format/*    ...*/); // Creates a listbox      with a formatted options string
TKC_WIDGET tkc_menu(char * format/*       ...*/); // Creates a menu         with a formatted options string
TKC_WIDGET tkc_menubutton(char * format/* ...*/); // Creates a menubutton   with a formatted options string
TKC_WIDGET tkc_message(char * format/*    ...*/); // Creates a message      with a formatted options string
TKC_WIDGET tkc_optionmenu(char * format/* ...*/); // Creates a optionmenu   with a formatted options string
TKC_WIDGET tkc_panedwindow(char * format/*...*/); // Creates a panedwindow  with a formatted options string
TKC_WIDGET tkc_radiobutton(char * format/*...*/); // Creates a radiobutton  with a formatted options string
TKC_WIDGET tkc_scale(char * format/*      ...*/); // Creates a scale        with a formatted options string
TKC_WIDGET tkc_scrollbar(char * format/*  ...*/); // Creates a scrollbar    with a formatted options string
TKC_WIDGET tkc_spinbox(char * format/*    ...*/); // Creates a spinbox      with a formatted options string
TKC_WIDGET tkc_text(char * format/*       ...*/); // Creates a text         with a formatted options string
int        tkc_command(char * name, int function()); // Creates a Tcl command and links it to a C fucntion
int        tkc_pack(TKC_WIDGET widget, char * options); // Packs a widget with an options string
int        tkc_place(TKC_WIDGET widget, int x, int y, char * options); // Places a widget at x and y with an options string
int        tkc_grid(TKC_WIDGET widget, int x, int y, int w, int h, char * options); // Grids a widget with x,y,w,h and an options string
int        tkc_set_arc_theme(); // set an arc theme for some widgets

// ---------------------------------------------------------------------------------- Init and quit functions
int tkc_init()
{
	interp = Tcl_CreateInterp();
	Tcl_Init(interp);
	tkc_stdout = Tcl_GetStdChannel(TCL_STDOUT);
	return 0;
}

int tkc_mainloop()
{
	Tk_MainLoop();
	Tcl_DeleteInterp(interp);
	return 0;
}

// ----------------------------------------------------------------------------------------- Utility commands
int tkc_puts(char * output)
{
	Tcl_Write(tkc_stdout, output, -1);
	Tcl_Write(tkc_stdout, "\n", -1);
	Tcl_Flush(tkc_stdout);
	return 0;
}

#define tkc_execute(command_format, ...) tkc_execute_object(Tcl_ObjPrintf(command_format, ##__VA_ARGS__) ) ; 

int tkc_execute_object(Tcl_Obj * command)
{
 	Tcl_IncrRefCount(command);
	if ( Tcl_EvalObj(interp, command) != TCL_OK )
	{
		Tcl_Obj * output = Tcl_ObjPrintf("%s: %s\n", command->bytes, Tcl_GetStringResult(interp));
		Tcl_WriteObj(tkc_stdout, output);
		Tcl_DecrRefCount(output);
		Tcl_Flush(tkc_stdout);
	}
	Tcl_DecrRefCount(command);
	return 0;
}

#define tkc_configure(widget, options_format, ...) tkc_formatted_configure(widget, Tcl_ObjPrintf(options_format, ##__VA_ARGS__)  ) ; 

int tkc_formatted_configure(TKC_WIDGET widget, Tcl_Obj * options)
{
	Tcl_Obj * command = Tcl_ObjPrintf("%s configure %s", widget, options->bytes);
	tkc_execute_object(command);
	Tcl_DecrRefCount(options);
	return 0;
}

// --------------------------------------------------------------------------------------- Creation functions
int tkc_window_(const char * title, const char * geometry, const char * resizable)
{
	tkc_execute
		(
			"package require Tk;"
			"wm title . %s;"
			"wm geometry . %s;"
			"wm resizable . %s;"
			"proc quit {} {\n"
			"	exit\n"
			"};"
			"bind . <Escape> quit;", 
			title,
			geometry,
			resizable	 	
		);
	return 0;
}

TKC_WIDGET tkc_create(char * type, Tcl_Obj * options)
{
	char * widget_id = Tcl_GetString( Tcl_ObjPrintf(".widget_0x%010x", tkc_widget_count++) );\
	Tcl_Obj * command = Tcl_ObjPrintf("%s %s %s", type, widget_id, options->bytes);\
	tkc_execute_object(command);\
	Tcl_DecrRefCount(options);
	return widget_id;
}

#define tkc_button(format, ...)     tkc_create("button",     Tcl_ObjPrintf(format, ##__VA_ARGS__))
#define tkc_canvas(format, ...)     tkc_create("canvas",     Tcl_ObjPrintf(format, ##__VA_ARGS__))
#define tkc_checkbutton(format, ...)tkc_create("checkbutton",Tcl_ObjPrintf(format, ##__VA_ARGS__))
#define tkc_entry(format, ...)      tkc_create("entry",      Tcl_ObjPrintf(format, ##__VA_ARGS__))
#define tkc_frame(format, ...)      tkc_create("frame",      Tcl_ObjPrintf(format, ##__VA_ARGS__))
#define tkc_label(format, ...)      tkc_create("label",      Tcl_ObjPrintf(format, ##__VA_ARGS__))
#define tkc_labelframe(format, ...) tkc_create("labelframe", Tcl_ObjPrintf(format, ##__VA_ARGS__))
#define tkc_listbox(format, ...)    tkc_create("listbox",    Tcl_ObjPrintf(format, ##__VA_ARGS__))
#define tkc_menu(format, ...)       tkc_create("menu",       Tcl_ObjPrintf(format, ##__VA_ARGS__))
#define tkc_menubutton(format, ...) tkc_create("menubutton", Tcl_ObjPrintf(format, ##__VA_ARGS__))
#define tkc_message(format, ...)    tkc_create("message",    Tcl_ObjPrintf(format, ##__VA_ARGS__))
#define tkc_optionmenu(format, ...) tkc_create("optionmenu", Tcl_ObjPrintf(format, ##__VA_ARGS__))
#define tkc_panedwindow(format, ...)tkc_create("panedwindow",Tcl_ObjPrintf(format, ##__VA_ARGS__))
#define tkc_radiobutton(format, ...)tkc_create("radiobutton",Tcl_ObjPrintf(format, ##__VA_ARGS__))
#define tkc_scale(format, ...)      tkc_create("scale",      Tcl_ObjPrintf(format, ##__VA_ARGS__))
#define tkc_scrollbar(format, ...)  tkc_create("scrollbar",  Tcl_ObjPrintf(format, ##__VA_ARGS__))
#define tkc_spinbox(format, ...)    tkc_create("spinbox",    Tcl_ObjPrintf(format, ##__VA_ARGS__))
#define tkc_text(format, ...)       tkc_create("text",       Tcl_ObjPrintf(format, ##__VA_ARGS__))
#define tkc_widget(format, ...)     tkc_create("widget",     Tcl_ObjPrintf(format, ##__VA_ARGS__))

int tkc_command(char * name, int function())
{
	Tcl_CreateCommand(interp, name, function, NULL, NULL);
	return 0;
}

// ----------------------------------------------------------------------------------------- Layout functions
int tkc_pack(TKC_WIDGET widget, char * options)
{
	return tkc_execute("pack %s %s", widget, options);
}

int tkc_place(TKC_WIDGET widget, int x, int y, char * options)
{
	return tkc_execute("place %s -x %d -y %d %s", widget, x, y, options);
}

int tkc_grid(TKC_WIDGET widget, int x, int y, int w, int h, char * options)
{
	return tkc_execute("grid %s -row %d -column %d -columnspan %d -rowspan %d %s", widget, x, y, w, h, options);
}

// -------------------------------------------------------------------------------------------------- Theming
int tkc_set_arc_theme()
{
	return tkc_execute
		(
		 	". configure -background #383c4a\n"
			"option add *Button.background #444a58\n"
			"option add *Button.foreground #b3bac4\n"
			"option add *Button.activeBackground #505666\n"
			"option add *Button.activeForeground #b3bac4\n"
			"option add *Button.highlightColor #5294e2\n"
			"option add *Button.highlightBackground #2b2e39\n"
			"option add *Button.borderWidth 0\n"
			"option add *Button.relief flat\n"
			"option add *Button.overRelief flat\n"
			"option add *Button.padY 1\n"

			"option add *Entry.background #404552\n"
			"option add *Entry.foreground #b3bac4\n"
			"option add *Entry.selectBackground #5294e2\n"
			"option add *Entry.selectForeground #ffffff\n"
			"option add *Entry.readonlyBackground #404552\n"
			"option add *Entry.highlightColor #5294e2\n"
			"option add *Entry.highlightBackground #2c303a\n"
			"option add *Entry.relief flat\n"
			"option add *Entry.insertBackground #d3dae3\n"

			"option add *Frame.borderWidth 1\n"
			"option add *Frame.background #2c303a\n"
	
			"option add *Label.background #383c4a\n"
			"option add *Label.foreground #ccd3db\n"
			"option add *Label.relief flat\n"

			"option add *Text.background #404552\n"
			"option add *Text.foreground #d3dae3\n"
			"option add *Text.selectBackground #5294e2\n"
			"option add *Text.selectForeground #ffffff\n"
			"option add *Text.highlightColor #5294e2\n"
			"option add *Text.highlightBackground #2c303a\n"
			"option add *Text.relief flat\n"
			"option add *Text.insertBackground #d3dae3\n"

			"option add *Spinbox.highlightColor #2c303a\n"
			"option add *Spinbox.highlightBackground #2c303a\n"
			"option add *Spinbox.relief flat\n"
			"option add *Spinbox.background #404552\n"
			"option add *Spinbox.foreground #b3bac4\n"
			"option add *Spinbox.selectBackground #5294e2\n"
			"option add *Spinbox.selectForeground #ffffff\n"
			"option add *Spinbox.buttonBackground #444a58\n"
			"option add *Spinbox.insertBackground #d3dae3\n"

			"option add *Checkbutton.background #383c4a\n"
			"option add *Checkbutton.foreground #d3dae3\n"
			"option add *Checkbutton.activeBackground #383c4a\n"
			"option add *Checkbutton.activeForeground #d3dae3\n"
			"option add *Checkbutton.selectColor #2d323d\n"
			"option add *Checkbutton.highlightColor #5294e2\n"
			"option add *Checkbutton.highlightBackground #383c4a\n"
			"option add *Checkbutton.relief flat\n"
		);
}
